package examples.bombing;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;

import java.util.HashMap;
import java.util.Map;

/**
 * Class description goes here
 * 
 * @author Artjom Kruglenkov
 * @version 1.0
 */
public class SmartBomb extends Agent {

    private static final long serialVersionUID = 1L;
    // The expected number of casualties after detonation
    private int numberOfCasualties;
    // Bombs in a group
    private Map<AID, Boolean> bombsGroup;

    // Agent initialization
    protected void setup() {
        Object[] args = getArguments();

        if (args != null && args.length > 0) {

            numberOfCasualties = Integer.parseInt(args[0].toString());
            System.out.println("Hello! Bomb-agent " + getAID().getName() + " is ready (" + numberOfCasualties + ").");

            bombsGroup = new HashMap<AID, Boolean>();
            bombsGroup.put(getAID(), false);

            DFAgentDescription dfd = new DFAgentDescription();
            dfd.setName(getAID());
            ServiceDescription sd = new ServiceDescription();
            sd.setType("bomb-detonation");
            sd.setName("JADE-chain-bombing");
            dfd.addServices(sd);

            try {
                DFService.register(this, dfd);
            } catch (FIPAException e) {
                e.printStackTrace();
                throw new RuntimeException("Can't register smart bomb!");
            }

            addBehaviour(new TickerBehaviour(this, 1000) {

                private static final long serialVersionUID = 1L;

                @Override
                protected void onTick() {

                    DFAgentDescription template = new DFAgentDescription();
                    ServiceDescription sd = new ServiceDescription();
                    sd.setType("bomb-detonation");
                    template.addServices(sd);

                    try {
                        DFAgentDescription[] results = DFService.search(myAgent, template);
                        for (DFAgentDescription desc : results) {
                            if (!bombsGroup.containsKey(desc.getName())) {
                                bombsGroup.put(desc.getName(), false);
                            }
                        }
                    } catch (FIPAException e) {
                        e.printStackTrace();
                    }

                    if (numberOfCasualties >= 1000) {
                        bombsGroup.put(getAID(), true);
                    }

                    for (AID bomb : bombsGroup.keySet()) {
                        if (!getAID().equals(bomb)) {
                            ACLMessage msg = new ACLMessage(ACLMessage.INFORM);
                            msg.addReceiver(bomb);
                            msg.setContent(bombsGroup.get(getAID()).toString());
                            send(msg);
                        }
                    }

                    if (getArmedCount() >= 3) {
                        myAgent.addBehaviour(new BlastBehaviour());
                    }
                }
            });

            addBehaviour(new CyclicBehaviour() {

                private static final long serialVersionUID = 1L;

                @Override
                public void action() {
                    MessageTemplate mt = MessageTemplate.MatchPerformative(ACLMessage.INFORM);
                    ACLMessage msg = myAgent.receive(mt);
                    if (msg != null) {
                        AID bomb = msg.getSender();
                        String isArmed = msg.getContent();

                        if (isArmed.equals("true")) {
                            bombsGroup.put(bomb, true);
                        }
                    } else {
                        block();
                    }
                }
            });
        } else {
            System.out.println("The number of people wasn't specified...");
            doDelete();
        }
    }

    protected void takeDown() {
        try {
            DFService.deregister(this);
        } catch (FIPAException e) {
            e.printStackTrace();
        }
        System.out.println("Bomb-agent " + getAID().getName() + " terminating.");
    }

    private int getArmedCount() {
        int count = 0;
        for (Boolean armed : bombsGroup.values()) {
            if (armed == true) {
                count++;
            }
        }
        return count;
    }

    private class BlastBehaviour extends OneShotBehaviour {
        private static final long serialVersionUID = 1L;

        @Override
        public void action() {
            System.out.println(myAgent.getName() + ":BOOOOOOOM!!!");
            doDelete();
        }
    }
}
